﻿using Microsoft.Practices.Prism.Commands;
using System;
using System.Windows.Input;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Client.ViewModels
{
    using Domain.Common.Site;
    using Domain.ClientService;
    using Common;
    using dm = Domain.Models;
    [Serializable]
    [XmlRoot("odds")]
    public class Odds : CommandViewModel, IDisposable
    {
        #region events & raise events
        public event Action<Odds, BetInfo> OnQueryBetInfoOver;
        public event Action<Odds, BetItem> OnQueryBetInfoBegin;

        protected virtual void RaiseOnQueryBetInfoBegin(BetItem betTo)
        {
            if (OnQueryBetInfoBegin != null)
            {
                OnQueryBetInfoBegin(this, betTo);
            }
        }

        protected virtual void RaiseQueryBetInfoOver(BetInfo bif)
        {
            if (OnQueryBetInfoOver != null)
            {
                OnQueryBetInfoOver(this, bif);
            }
        }
        #endregion

        #region command
        private ICommand _queryBetInfoCommand;
        /// <summary>
        /// 查询投注信息命令
        /// </summary>
        public ICommand QueryBetInfoCommand
        {
            get
            {
                return _queryBetInfoCommand;
            }
        }
        #endregion

        #region cotr & private porperty
        private IBetSiteProxyService siteManager;
        public IBetSiteProxyService SiteManager
        {
            get
            {
                return siteManager;
            }
            set
            {
                siteManager = value;
            }
        }

        private Factory.IViewModelFactory viewModelFactory;
        public Factory.IViewModelFactory ViewModelFactory
        {
            get { return viewModelFactory; }
            set { viewModelFactory = value; }
        }
        

        private bool localizationOdds = false;
        public bool LocalizationOdds
        {
            get { return localizationOdds; }
        }

        private static Dictionary<dm.Odds.MarketType, string> OddsTypeNameMapping = new Dictionary<dm.Odds.MarketType, string>()
        {
            {dm.Odds.MarketType.GOU, "全场大小"},
            {dm.Odds.MarketType.G1X2, "全场1X2"},
            {dm.Odds.MarketType.GHDP, "全场让球"},
            {dm.Odds.MarketType.FHOU, "上半场 大小"},
            {dm.Odds.MarketType.FH1X2, "上半场 1X2"},
            {dm.Odds.MarketType.FHHDP, "上半场 让球"}
        };

        public Odds(IBetSiteProxyService _siteManager, Factory.IViewModelFactory _viewModelFactory)
        {
            siteManager = _siteManager;
            viewModelFactory = _viewModelFactory;
            _queryBetInfoCommand = new DelegateCommand<BetItem?>(this.QueryBetInfo);
            RegisterCommand(QueryBetInfoCommand, "查询BetInfo");
        }
        #endregion

        #region command imple
        protected void QueryBetInfo(BetItem? betTo)
        {
            if (!betTo.HasValue) return;
            RaiseOnQueryBetInfoBegin(betTo.Value);
            QueryBetInfo(betTo.Value, true);
        }
        #endregion

        #region local method
        public void QueryBetInfo(BetItem betTo, bool focus)
        {
            if (siteManager == null) return;
            var loginCheck = siteManager.HasLogin(BmID);
            Task.WaitAll(loginCheck);
            if (!loginCheck.Result)
                return;
            RaiseCommandStart(QueryBetInfoCommand, "开始查询BetInfo");
            var price = TryGetOddsValue(betTo);
            dm.OddsPrice.OddsPriceType pType = CaseBetToType(betTo);
            dm.OddsPrice oddsPrice = new dm.OddsPrice(price, pType);
            oddsPrice.MkType = Type;
            var tickTask = siteManager.RequestTick(BmID, GameID, Type, oddsPrice, OValue, OddsID);
            tickTask.ContinueWith(t =>
            {
                RaiseCommandOver(QueryBetInfoCommand);
                if (t.IsCanceled || t.Exception != null)
                {
                    RaiseDispatcherCommandError(t.Exception);
                    return;
                }
                if (t.Result == null)
                    return;

                BetApplication.Current.Dispatcher.Invoke(()=>
                {
                    MappingBetTickToInfo(t.Result, betTo, focus);
                });
            });

        }

        private decimal TryGetOddsValue(BetItem? betTo)
        {
            if (!betTo.HasValue) return 0.0m;
            decimal oddsValue = 0.0m;
            switch (betTo)
            {
                case BetItem.HomeOrOver:
                    oddsValue = HValue;
                    break;
                case BetItem.AwayOrUnder:
                    oddsValue = AValue;
                    break;
                case BetItem.Tie:
                    oddsValue = OValue;
                    break;
                default:
                    break;
            }
            return oddsValue;
        }

        private dm.OddsPrice.OddsPriceType CaseBetToType(BetItem betTo)
        {
            dm.OddsPrice.OddsPriceType pType;
            if (Type == dm.Odds.MarketType.FHOU || Type == dm.Odds.MarketType.GOU)
            {
                if (betTo == BetItem.HomeOrOver)
                    pType = dm.OddsPrice.OddsPriceType.Over;
                else
                    pType = dm.OddsPrice.OddsPriceType.Under;
            }
            else
            {
                switch (betTo)
                {
                    case BetItem.Tie:
                        pType = dm.OddsPrice.OddsPriceType.Tie;
                        break;
                    case BetItem.HomeOrOver:
                        if (!Reversal)
                            pType = dm.OddsPrice.OddsPriceType.Home;
                        else
                            pType = dm.OddsPrice.OddsPriceType.Away;
                        break;
                    case BetItem.AwayOrUnder:
                        if (!Reversal)
                            pType = dm.OddsPrice.OddsPriceType.Away;
                        else
                            pType = dm.OddsPrice.OddsPriceType.Home;
                        break;
                    default:
                        pType = dm.OddsPrice.OddsPriceType.Tie;
                        break;
                }
            }

            return pType;
        }

        private void MappingBetTickToInfo(dm.BetTick tick, BetItem betTo, bool focus)
        {
            var rawTitle = OddsTypeNameMapping.ContainsKey(Type) ? OddsTypeNameMapping[Type] : string.Empty;
            _betInfo = viewModelFactory.CreateOrGet<BetInfo>();
            AutoMapper.Mapper.Map(tick, _betInfo);
            _betInfo.RawTitle = rawTitle;
            _betInfo.League = Game.League;
            _betInfo.Type = this.Type;
            _betInfo.BetTo = betTo;
            _betInfo.LastTick = tick;
            RaiseQueryBetInfoOver(_betInfo);
            RaisePropertyChanged("BetInfo");
            _betInfo.IsFocus = focus;
        }

        public decimal CalcMSValueFloat(decimal newv, decimal oldv)
        {
            decimal covnew = newv < 0 ? (1 / newv) * -1 : newv;
            decimal covold = oldv < 0 ? (1 / oldv) * -1 : oldv;
            return Math.Round(covnew - covold, 3);
        }

        #endregion

        #region property
        private BetInfo _betInfo;
        [XmlIgnore]
        public BetInfo BetInfo
        {
            get
            {
                return _betInfo;
            }
        }

        private string _oddsID;
        [XmlAttribute("id")]
        public string OddsID
        {
            get { return _oddsID; }
            set
            {
                PropertyValueChange(ref _oddsID, value, "OddsID");
                if (!string.IsNullOrEmpty(_oddsID) && !localizationOdds)
                {
                    localizationOdds = true;
                    RaisePropertyChanged("LocalOdds");
                }
                else if (localizationOdds)
                {
                    localizationOdds = false;
                    RaisePropertyChanged("LocalOdds");
                }
            }
        }

        private string _gameID;
        [XmlAttribute("game-id")]
        public string GameID
        {
            get { return _gameID; }
            set
            {
                PropertyValueChange(ref _gameID, value, "GameID");
            }
        }

        private Game _game;
        [XmlIgnore]
        public Game Game
        {
            get { return _game; }
            set
            {
                PropertyValueChange(ref _game, value, "Game");
            }
        }

        private byte _bmID;
        [XmlAttribute("bm-id")]
        public byte BmID
        {
            get { return _bmID; }
            set
            {
                PropertyValueChange(ref _bmID, value, "BmID");
            }
        }

        private dm.Odds.MarketType _type;
        [XmlAttribute("type")]
        public dm.Odds.MarketType Type
        {
            get { return _type; }
            set
            {
                PropertyValueChange(ref _type, value, "Type");
            }
        }

        private decimal _hValue;
        [XmlAttribute("h")]
        public decimal HValue
        {
            get { return _hValue; }
            set
            {
                if (_hValue != value)
                {
                    RaisePropertyChanging("HFloat");
                    _hFloat = CalcMSValueFloat(value, _hValue);
                }
                PropertyValueChange(ref _hValue, value, "HValue");
                RaisePropertyChanged("HFloat");
            }
        }

        private decimal _hFloat;
        [XmlAttribute("h-float")]
        public decimal HFloat
        {
            get { return _hFloat; }
            set
            {
                PropertyValueChange(ref _hFloat, value, "HFloat");
            }
        }

        private decimal _aValue;
        [XmlAttribute("a")]
        public decimal AValue
        {
            get { return _aValue; }
            set
            {
                if (_aValue != value)
                {
                    RaisePropertyChanging("AFloat");
                    _aFloat = CalcMSValueFloat(value, _aValue);
                }
                PropertyValueChange(ref _aValue, value, "AValue");
                RaisePropertyChanged("AFloat");
            }
        }

        private decimal _aFloat;
        [XmlAttribute("a-float")]
        public decimal AFloat
        {
            get { return _aFloat; }
            set
            {
                PropertyValueChange(ref _aFloat, value, "AFloat");
            }
        }

        private decimal _oValue;
        [XmlAttribute("o")]
        public decimal OValue
        {
            get { return _oValue; }
            set
            {
                if (_oValue != value)
                {
                    RaisePropertyChanging("OFloat");
                    _oFloat = CalcMSValueFloat(value, _oValue);
                }
                PropertyValueChange(ref _oValue, value, "OValue");
                RaisePropertyChanged("OFloat");
            }
        }

        private decimal _oFloat;
        [XmlAttribute("o-float")]
        public decimal OFloat
        {
            get { return _oFloat; }
            set
            {
                PropertyValueChange(ref _oFloat, value, "OFloat");
            }
        }
        #endregion

        private bool _reversal;
        [XmlAttribute("reversal")]
        public bool Reversal
        {
            get { return _reversal; }
            set
            {
                PropertyValueChange(ref _reversal, value, "Reversal");
            }
        }
        

        public class PriceValue
        {
            protected PriceValue()
            {

            }

            public static PriceValue CreateFromMS(decimal price)
            {
                decimal asPrice = price < 0 ? (1 / price) * -1 : price;
                return new PriceValue
                {
                    MSPrice = price,
                    ASPrice = asPrice
                };
            }

            public static PriceValue CreateFromAS(decimal price)
            {
                decimal msPrice = price < 1 ? price : (1 / price) * -1;
                return new PriceValue
                {
                    ASPrice = price,
                    MSPrice = msPrice
                };
            }

            public static PriceValue Max(params PriceValue[] prices)
            {
                PriceValue max = prices[0];
                for (int i = 1; i < prices.Length; i++)
                {
                    max = max.ASPrice < prices[i].ASPrice ? prices[i] : max;
                }
                return max;
            }

            public static PriceValue Min(params PriceValue[] prices)
            {
                PriceValue min = prices[0];
                for (int i = 1; i < prices.Length; i++)
                {
                    min = min.ASPrice > prices[i].ASPrice ? prices[i] : min;
                }
                return min;
            }

            public decimal MSPrice { get; protected set; }

            public decimal ASPrice { get; protected set; }
        }

        public void Dispose()
        {
            
        }
    }
}
